package netwrokproject.transporte;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.LinkedList;

import netwrokproject.redes.Rede;

import netwrokproject.Layer;

public class Transporte extends Layer {
    
    ArrayList<Porta> portas = new ArrayList<Porta>();

    LinkedList<MensagemTransporte> candidatasRT = new LinkedList<MensagemTransporte>();

    ArrayDeque<MensagemTransporte> msgsToBeSent = new ArrayDeque<MensagemTransporte>();

    static final byte RST = 4;

    public Transporte(){
        super();
    }

    public Transporte(String nome){
        super(nome);
        byte ipOrigem=12,ipDestino=12;
        Rede r = new Rede(ipOrigem,ipDestino);
        setLayerDOWN(r);
        this.layerDOWN = r;
        this.layerDOWN.setLayerUP(this);
        ((Rede) this.layerDOWN).estado.camadaCima =this;
        this.layerDOWN.start();
    }

    public Porta bind(byte porta) throws Exception {
        Porta p = new Porta(porta, this);
        if(portas.contains(p)) throw new Exception("Porta ocupada.");
        else {
            portas.add(p);
            p.start();
            return p;
        }
    }

    public short getMaiorAck() throws Exception {

        short ackfinal = -1;
        if (candidatasRT.isEmpty()) throw new Exception("Lista de canditadas a retransmissao vazia.");
        for (MensagemTransporte msg: candidatasRT){
            if (ackfinal < msg.getAck()) ackfinal = msg.getAck();
        }
        return ackfinal;

    }

    public void LimparCandidatas(short ackatual) throws Exception {

        if (candidatasRT.isEmpty()) throw new Exception("Lista de canditadas a retransmissao vazia.");
        for (MensagemTransporte msg: candidatasRT){
            if (ackatual > msg.getAck()) candidatasRT.remove(msg);
        }
    }

    public void retransmitir() {
        for (MensagemTransporte msg: candidatasRT){
            msgsToBeSent.addLast(msg);
        }
    }

    private void reset(MensagemTransporte msg) {

        byte flags = RST;
        MensagemTransporte rst = new MensagemTransporte(msg.getIpDestino(),
                msg.getPortaDestino(), msg.getIpOrigem(), msg.getPortaOrigem(),
                (short) 0,(short) 0, flags, new ArrayDeque<Byte>());

        msgsToBeSent.addLast(rst);

    }

    @Override
    protected void sendMessage() {
    	System.out.println("SendMessage: Fila de mensagens, "+msgsToBeSent);
        if (msgsToBeSent.isEmpty()) return;
        else 
            for (MensagemTransporte msg: msgsToBeSent){
            	System.out.println("sendMessage: Mensagem sendo enviada para rede: "+msg);
                layerDOWN.send(msg.toArrayDeque());
                
            }
    }

    @Override
    protected void receiveMessage() {
        MensagemTransporte msg = new MensagemTransporte(receivedMessages.removeFirst());

        Porta p = portas.get(portas.indexOf(new Porta(msg.portaDestino, this)));
        if (p == null){
            reset(msg);
        } else {
            p.MensagensRecebidas.addLast(msg.toArrayDeque());
        }
    }

    public ArrayDeque<ArrayDeque<Byte>> getReceivedMessages() {
        return receivedMessages;
    }

}
